package utils;


import structure.ListenerNode;
import structure.Transaction4Packet;
import structure.TranscationRoute;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;

import static test.SimulationTest.MAX_NORMAL_NODE_NUMBER;
import static test.SimulationTest.MAX_TX_NUMBER;

/**
 * @author zhangjianzhong
 * @create 2021-11-21 11:53
 */
public class PearsonCorrelationScore {
    public double[][] scoreMatrix; //交易x交易的皮尔逊矩阵

    public PearsonCorrelationScore(int transactionArrayLength) {
        this.scoreMatrix = new double[transactionArrayLength][transactionArrayLength];
    }


    /**
     * 计算交易之间的皮尔逊相关矩阵，结果存在成员属性中，scoreMatrix[i][j]代表第i个和第j个交易之间的相关性
     * ij排序由传入参数normalNodeArray
     * @param listenerNode    提供数据,所有的转发记录，权重，时间戳都在listenerNode里了
     * @param normalNodeArray 用于提供全局有序的节点排列
     * @param transactionArray 用于提供全局有序的交易排列，很尴尬，交易是从hashMap里面取出来的，没有准确的顺序
     */
    public void caculateMatrix(ListenerNode listenerNode, ArrayList<String> normalNodeArray,ArrayList<String> transactionArray) {
        HashMap<String, Transaction4Packet> transListenMap = listenerNode.txListenSetPacketByTx;

        for (Map.Entry<String, Transaction4Packet> entry1 : transListenMap.entrySet()) {
            ArrayList<TranscationRoute> listAndWeight1 = entry1.getValue().listAndWeight;//取出权重列表
            //构造该交易的权重向量，需要是全局有序的
            double[] weightVetorOftx1 = new double[MAX_NORMAL_NODE_NUMBER];
            for (TranscationRoute transcationRoute : listAndWeight1) {
                String fromNode1 = transcationRoute.fromNode;
                double weight1 = transcationRoute.weight;
                weightVetorOftx1[normalNodeArray.indexOf(fromNode1)] = weight1;
            }

            for (Map.Entry<String, Transaction4Packet> entry2 : transListenMap.entrySet()) {
                ArrayList<TranscationRoute> listAndWeight2 = entry2.getValue().listAndWeight;
                //构造该交易的权重向量
                double[] weightVetorOftx2 = new double[MAX_NORMAL_NODE_NUMBER];
                for (TranscationRoute transcationRoute : listAndWeight2) {
                    String fromNode2 = transcationRoute.fromNode;
                    double weight2 = transcationRoute.weight;
                     weightVetorOftx2[normalNodeArray.indexOf(fromNode2)] = weight2;
                }
                //计算两个向量的皮尔逊相关系数
                int i = transactionArray.indexOf(entry1.getKey());
//                    if(i%200==0){
//                        System.out.println("矩阵计算进度："+(i+1)+"行");
//                }
                int j = transactionArray.indexOf(entry2.getKey());
                scoreMatrix[i][j] = getPearsonCorrelationScore(weightVetorOftx1, weightVetorOftx2);
            }
        }


//        for (Transaction4Packet pack1 : transListenMap.values()) {//遍历取出每一笔交易
//            ArrayList<TranscationRoute> listAndWeight1 = pack1.listAndWeight;//取出权重列表
//            //构造该交易的权重向量，需要是全局有序的
//            double[] weightVetorOftx1 = new double[MAX_NORMAL_NODE_NUMBER];
//            for (TranscationRoute transcationRoute : listAndWeight1) {
//                String fromNode1 = transcationRoute.fromNode;
//                double weight1 = transcationRoute.weight;
//                weightVetorOftx1[normalNodeArray.indexOf(fromNode1)] = weight1;
//            }
//            //再取出该交易的所有时间戳，这里就需要重新排序了
//            HashMap<String, Transaction4Packet> transListenMap2 = listenerNode.txListenSetPacketByTx;
//            int j = 0;
//            for (Transaction4Packet pack2 : transListenMap2.values()) {//遍历取出另一笔交易
//                ArrayList<TranscationRoute> listAndWeight2 = pack2.listAndWeight;
//                //构造该交易的权重向量
//                double[] weightVetorOftx2 = new double[MAX_NORMAL_NODE_NUMBER];
//                for (TranscationRoute transcationRoute : listAndWeight2) {
//                    String fromNode2 = transcationRoute.fromNode;
//                    double weight2 = transcationRoute.weight;
//                    weightVetorOftx2[normalNodeArray.indexOf(fromNode2)] = weight2;
//                }
//                //计算两个向量的皮尔逊相关系数
//                scoreMatrix[i][j++] = getPearsonCorrelationScore(weightVetorOftx1, weightVetorOftx2);
//                //System.out.println();
//            }
//            i++;
//        }
    }

    public static double getPearsonCorrelationScore(double[] xData, double[] yData) {
        if (xData.length != yData.length)
            throw new RuntimeException("数据不正确！");
        double xMeans;
        double yMeans;
        double numerator;// 求解皮尔逊的分子
        double denominator;// 求解皮尔逊系数的分母

        double result;
        // 拿到两个数据的平均值
        xMeans = getMeans(xData);
        yMeans = getMeans(yData);
        // 计算皮尔逊系数的分子
        numerator = generateNumerator(xData, xMeans, yData, yMeans);
        // 计算皮尔逊系数的分母
        denominator = generateDenomiator(xData, xMeans, yData, yMeans);
        // 计算皮尔逊系数
        result = numerator / denominator;
        return result;
    }

    /**
     * 计算分子
     */
    private static double generateNumerator(double[] xData, double xMeans, double[] yData, double yMeans) {
        double numerator = 0.0;
        for (int i = 0; i < xData.length; i++) {
            numerator += (xData[i] - xMeans) * (yData[i] - yMeans);
        }
        return numerator;
    }

    /**
     * 生成分母
     */
    private static double generateDenomiator(double[] xData, double xMeans, double[] yData, double yMeans) {
        double xSum = 0.0;
        for (double xDatum : xData) {
            xSum += (xDatum - xMeans) * (xDatum - xMeans);
        }
        double ySum = 0.0;
        for (double yDatum : yData) {
            ySum += (yDatum - yMeans) * (yDatum - yMeans);
        }
        return Math.sqrt(xSum) * Math.sqrt(ySum);
    }

    /**
     * 根据给定的数据集进行平均值计算
     */
    private static double getMeans(double[] datas) {
        double sum = 0.0;
        for (double data : datas) {
            sum += data;
        }
        return sum / datas.length;
    }

    public void printMatrix() {
        java.text.DecimalFormat df = new java.text.DecimalFormat("0.00");
        for (int i = 0; i < MAX_TX_NUMBER; i++) {
            for (int j = 0; j < MAX_TX_NUMBER; j++) {
                System.out.print(df.format(this.scoreMatrix[i][j]) + " ");
            }
            System.out.println();
        }
    }

    public static double[][] readFile(String filename) {
        double[][] arr = new double[4][3];

        try {
            File file = new File(filename);
            Scanner scanner = new Scanner(file);
            while (scanner.hasNextLine()) {
                for (int i = 0; i < arr.length; i++) {
                    String[] line = scanner.nextLine().split(" ");
                    for (int j = 0; j < line.length; j++) {
                        arr[i][j] = Double.parseDouble(line[j]);
                    }
                }

            }
            scanner.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return arr;
    }

    public static void main(String[] args) {
        HashMap<Integer, Double> mapX = new HashMap<>();
        mapX.put(1, 1.23);
        mapX.put(2, 1.56);
        mapX.put(3, 5.77);
        double[] x = new double[]{0, 0, 0, 1.23, 1.56, 5.77};
        double[] y = new double[]{8.15, 3.77, 1.000, 0, 0, 0};
        System.out.println(getPearsonCorrelationScore(x, y));
    }
}